/*
 * Copyright (C) 2010 CODEBISTRO LLC, Sunnyvale, California.
 * All rights reserved. Proprietary and Confidential.
 */

package org.codebistro.util;

import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Strings14 {
	protected static final Pattern PATTERN_IDENTIFIER = Pattern.compile("^[A-Za-z]([A-Za-z0-9_\\.])+$");

	/**
	 * If 'value' starts with 'prefix', cut the 'prefix' off and return 
	 */
	public static String unprefix(String value, String prefix) {
		if (value==null)
			return value;
		if (value.startsWith(prefix))
			return value.substring(prefix.length());
		else
			return value;
	}

	/**
	 * Return the 'input' limited to 'limit' characters. Add
	 * 'elipsis' at the end.
	 */
	public static String limit(String input, int limit, String elipsis) {
		if (input==null)
			return null;
		if (input.length() > limit) {
			String result= input.substring(0, limit);
			if (elipsis!=null)
				result+=elipsis;
			return result;
		} else
			return input;
	}
	
	public static String limit(String input, int limit) {
		return limit(input, limit, null);
	}

	public static boolean isValidEntityName(final String name) {
		return Pattern.compile("^(\\p{Alnum}| |\t|\\.|,|')+$").matcher(name).matches();
	}

	static final String PATTERN = "^[A-Za-z0-9]+([-_\\.]*[A-Za-z0-9]+)*@[A-Za-z0-9]+([-_\\.]*[A-Za-z0-9]+)*(\\.[_A-Za-z]{2,6})$";

	public static boolean isValidEmail(final String emailAddress) {
		return emailAddress.matches(PATTERN);
	}

	public static boolean isValidIdentifier(final String someIdentifier) {
		return PATTERN_IDENTIFIER.matcher(someIdentifier).matches();
	}

	public static String dumpStrings(final String[] strings) {
		StringBuffer dump= new StringBuffer();
		for (int i = 0; i < strings.length; i++) {
			dump.append("'").append(strings[i]).append("'").append(i == strings.length - 1 ? "" : ",");
		}
		return dump.toString();
	}

	public static String padLeft(String original, String s, int minLen) {
		if (original.length() >= minLen) {
			return original;
		}

		final StringBuffer result = new StringBuffer(original);

		while (result.length() < minLen) {
			result.insert(0, s);
		}

		return result.toString();
	}

	/**
	 * Convenience for appending strings via separator
	 */
	public static class Appender {
		String separator;
		StringBuffer buffer;
		
		public Appender(String separator) {
			this.separator= separator;
		}
		
		public void append(String value) {
			if (buffer!=null) {
				buffer.append(separator);
				buffer.append(value);
			} else {
				buffer= new StringBuffer(value);
			}
		}
		
		public String toString() {
			if (buffer!=null)
				return buffer.toString();
			else
				return "";	// TODO should it be null?
		}
	}

	/**
	 * Count how many times characters from the set can be found in source string
	 */
	static public int count(String source, String set) {
		int count= 0;
		for(int i= 0; i < source.length(); i++) {
			if (set.indexOf(source.charAt(i))>=0)
				count+=1;
		}
		return count;
	}
	
	static final String HTML_CHARS= "<>";
	
	/**
	 * Strip what looks like HTML/XML tags from the input string. Employ simple euristic to detect tags.
	 */
	public static String stripTags(String in) {
		int startFrom= 0;
		StringBuffer out= new StringBuffer(in.length());
		while(startFrom < in.length()) {
			int startTag= in.indexOf('<', startFrom);
			if (startTag < 0) {
				out.append(in.substring(startFrom));
				break;
			} else {
				out.append(in.substring(startFrom, startTag));
				int endTag= in.indexOf('>', startTag + 1);
				if ( endTag>=0 && (endTag - startTag) < 10)
					startFrom= endTag + 1;
				else {
					out.append(in.subSequence(startTag, startTag + 1));
					startFrom= startTag + 1;
				}
			}	
		}
		return out.toString();
	}
	
	/**
	 * Convert collection to an array of strings using .toString() on each item
	 */
	public static String[] collectionToArray(Collection coll) {
		String[] result= new String[coll.size()];
	    int i= 0;
	    for(Iterator it= coll.iterator(); it.hasNext();) {
			Object field= it.next();
	    	result[i++]= field.toString();
		}
	    return result;

	}

	
	final static String FIELD_SEPARATORS= ",;";	
	final static String QUOTES= "\"'";	
	final static String SPECIAL_CHARS= FIELD_SEPARATORS + QUOTES;
	final static String WHITESPACE= " \t";
	
	/**
	 * Search 'source' for first character that is in 'charSet',
	 * starting from 'startFrom'.
	 * @return index or -1
	 * 
	 */
	static public int findFirstOf(String source, String charSet, int startFrom) {
		for(int i= startFrom; i < source.length(); i++) {
			if (charSet.indexOf(source.charAt(i)) >= 0)
				return i;
		}
		return -1;
	}

	/**
	 * Search 'source' for first character that is in 'charSet'.
	 * @return index or -1
	 */
	static public int findFirstOf(String source, String charSet) {
		return findFirstOf(source, charSet, 0);
	}
	
	public static final String EMPTY= "";
	
	/** Replace null strings on input by empty ones */ 
	public static String emptyIfNull(String in) {
		return in == null ? EMPTY : in;
	}

    public static boolean isNullOrEmpty(String string) {
      return string == null || string.length() == 0; // string.isEmpty() in Java 6
    }

    /**
     * @deprecated Exceeded authority here. Use either #isNullOrEmpty() or #isWhiteSpace()
     */
	public static boolean isEmpty(String value) {
        return isWhiteSpace(value);
	}

	public static boolean isWhiteSpace(String value) {
		return value == null || value.trim().length() <= 0;
	}

	/** Return 'in' enclosed in double quotes */
	public static String dQuote(String in) {
		return "\"" + in + "\"";
	}
	
	static String capture(String pattern) {
		return "(" + pattern + ")";
	}
	static String optional(String pattern) {
		return pattern + "?";
	}
	
	static String NUMBER= "-?\\d+";
	static String MODIFIER= "\\w*";
	
	static Pattern MODIFIED_NUMBER= Pattern.compile(capture(NUMBER) + optional(capture(MODIFIER)));

	static final long K= 1024;
	static final long M= K * K;
	static final long G= M * K;
	static final long T= G * K;
	
	public static long parseWithModifiers(String value) {
		return parseWithModifiers(value, 0); //	TODO remove the below method
	}

	public static long parseWithModifiers(String value, long defaultValue) {
		Matcher matcher= MODIFIED_NUMBER.matcher(value);
		if (!matcher.matches())
			throw new Bark("invalidFormat", value);
		long result= Long.parseLong(matcher.group(1));
		String modifier= matcher.group(2);
		if (modifier!=null && modifier.length()>0) {
			if ("K".equals(modifier))
				result*=K;
			else if ("M".equals(modifier))
				result*=M;
			else if ("G".equals(modifier))
				result*=G;
			else if  ("T".equals(modifier))
				result*=T;
			else
				throw new Bark("invalidFormat", value);
		}
		return result;			
	}

	/**
	 * This is not a regex-based method, it treats params as regular strings.
	 */
	public static String replaceAll(final String content, final String searchPattern, final String replacement) {
		if (searchPattern.equals(replacement)) {
			return content;
		}
	
		final String text = content;
		final String repl = searchPattern;
		final String with = replacement;
		int max = -1;
		
		if (isEmpty(text) || isEmpty(repl) || with == null || max == 0) {
			return text;
		}
		int start = 0;
		int end = text.indexOf(repl, start);
		if (end == -1) {
			return text;
		}
		int replLength = repl.length();
		StringBuffer buf = new StringBuffer(text.length());
		while (end != -1) {
			buf.append(text.substring(start, end)).append(with);
			start = end + replLength;
			if (--max == 0) {
				break;
			}
			end = text.indexOf(repl, start);
		}
		buf.append(text.substring(start));
		return buf.toString();
	}

	public static String join(Object[] array, String separator) {
		return join(array, separator, 0, array.length);
	}

	public static String join(Object[] array, String separator, int startIndex, int endIndex) {
		if (array == null) {
			return null;
		}
		if (separator == null) {
			separator = EMPTY;
		}

		// endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
		//           (Assuming that all Strings14 are roughly equally long)
		int bufSize = endIndex - startIndex;
		if (bufSize <= 0) {
			return EMPTY;
		}

		bufSize *= (array[startIndex] == null ? 16 : array[startIndex].toString().length()) + separator.length();

		StringBuffer buf = new StringBuffer(bufSize);

		for (int i = startIndex; i < endIndex; i++) {
			if (i > startIndex) {
				buf.append(separator);
			}
			if (array[i] != null) {     // Another bug: join(new String[] { null, "A"}, "," ==  ",A". should be "A"
				buf.append(array[i]);
			}
		}
		return buf.toString();
	}


    public static String capitalize(String str) {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return str;
        }
        return new StringBuffer(strLen)
            .append(Character.toTitleCase(str.charAt(0)))
            .append(str.substring(1))
            .toString();
    }

	public static String left(String str, int len) {
		if (str == null) {
			return null;
		}
		if (len < 0) {
			return EMPTY;
		}
		if (str.length() <= len) {
			return str;
		} else {
			return str.substring(0, len);
		}
	}

        public static String[] filterEmpty(final String[] strings) {
                List result = new ArrayList();

                for (int i = 0; i < strings.length; i++) {
                        if (!Strings14.isEmpty(strings[i])) {
                                result.add(strings[i]);
                        }
                }

                return (String[]) result.toArray(new String[result.size()]);
        }
}
